// PostgreSQL Database Management System
// (also known as Postgres, formerly known as Postgres95)
//
//  Portions Copyright (c) 2025, Supabase, Inc
//
//  Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
//
//  Portions Copyright (c) 1994, The Regents of the University of California
//
// Permission to use, copy, modify, and distribute this software and its
// documentation for any purpose, without fee, and without a written agreement
// is hereby granted, provided that the above copyright notice and this
// paragraph and the following two paragraphs appear in all copies.
//
// IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
// DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
// LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
// DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
// ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO
// PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
//
/*
 * PostgreSQL Parser Lexer - Token Definitions
 *
 * This file implements the core token system for the PostgreSQL lexer,
 * porting the token definitions from PostgreSQL's gram.y and scanner.h.
 * Ported from postgres/src/include/parser/scanner.h and postgres/src/backend/parser/gram.y
 *
 * IMPORTANT: These token constants are TEMPORARY scaffolding for Phase 2A development.
 * In Phase 3 (Grammar & Parsing), these will be REPLACED by goyacc-generated constants
 * from go/parser/grammar/postgres.y. The generated postgres.go file will contain the
 * authoritative token definitions that the lexer will import and use.
 *
 * This approach follows the Vitess pattern where the lexer uses token constants
 * generated by goyacc from the grammar file, not manually defined constants.
 *
 * Phase 2A: Manual constants (this file) - for lexer development and testing
 * Phase 3+: Generated constants from postgres.y - for parser integration
 */

package parser

import (
	"fmt"
	"strings"
)

// TokenType represents the type of a lexical token
// This is now just an alias for int to maintain compatibility during transition
// New code should use the generated parser constants directly
type TokenType = int

// Special token constants not generated by the parser
// These are used for lexer-internal operations
const (
	// Special tokens - internal to lexer only
	INVALID = -1 // Invalid token
	EOF     = 0  // End of file (aligns with yacc EOF expectation)

	// Note: Basic ASCII tokens (32-126) are represented by their character value
	// Note: All other tokens (IDENT, ICONST, keywords, etc.) are now defined
	//       in the generated postgres.go file and should be used directly
)

// TokenValue represents the union of possible token values
// This is the Go equivalent of PostgreSQL's core_YYSTYPE union
// Ported from postgres/src/include/parser/scanner.h:29-34
type TokenValue struct {
	// Only one of these fields should be set for any given token
	Ival    int    // For integer literals (ICONST, PARAM) - postgres/src/include/parser/scanner.h:31
	Str     string // For identifiers and non-integer literals - postgres/src/include/parser/scanner.h:32
	Keyword string // For canonical spelling of keywords - postgres/src/include/parser/scanner.h:33
}

// Token represents a single lexical token with its type, value, and position
type Token struct {
	Type     int        // The type of token (uses parser-generated constants)
	Value    TokenValue // The token's value
	Position int        // Byte offset from start of input (YYLTYPE equivalent)
	Text     string     // Raw text that produced this token
}

// NewToken creates a new token with the given parameters
func NewToken(tokenType int, position int, text string) *Token {
	return &Token{
		Type:     tokenType,
		Value:    TokenValue{Str: text},
		Position: position,
		Text:     text,
	}
}

// NewIntToken creates a new integer token
func NewIntToken(value int, position int, text string) *Token {
	return &Token{
		Type:     ICONST,                             // Uses generated parser constant
		Value:    TokenValue{Ival: value, Str: text}, // Set both for compatibility
		Position: position,
		Text:     text,
	}
}

// NewStringToken creates a new string token
func NewStringToken(tokenType int, value string, position int, text string) *Token {
	return &Token{
		Type:     tokenType,
		Value:    TokenValue{Str: value},
		Position: position,
		Text:     text,
	}
}

// NewKeywordToken creates a new keyword token
func NewKeywordToken(tokenType int, keyword string, position int, text string) *Token {
	return &Token{
		Type:     tokenType, // Use the keyword's specific token type
		Value:    TokenValue{Keyword: keyword, Str: strings.ToLower(keyword)},
		Position: position,
		Text:     text,
	}
}

// NewParamToken creates a new parameter token ($1, $2, etc.)
func NewParamToken(paramNum int, position int, text string) *Token {
	return &Token{
		Type:     PARAM, // Uses generated parser constant
		Value:    TokenValue{Ival: paramNum},
		Position: position,
		Text:     text,
	}
}

// IsStringLiteral returns true if the token is a string literal type
func (t *Token) IsStringLiteral() bool {
	return t.Type == SCONST || t.Type == USCONST
}

// IsNumericLiteral returns true if the token is a numeric literal type
func (t *Token) IsNumericLiteral() bool {
	return t.Type == ICONST || t.Type == FCONST
}

// IsBitStringLiteral returns true if the token is a bit string literal type
func (t *Token) IsBitStringLiteral() bool {
	return t.Type == BCONST || t.Type == XCONST
}

// IsOperator returns true if the token is an operator
func (t *Token) IsOperator() bool {
	return t.Type == Op ||
		t.Type == TYPECAST ||
		t.Type == DOT_DOT ||
		t.Type == COLON_EQUALS ||
		t.Type == EQUALS_GREATER ||
		t.Type == LESS_EQUALS ||
		t.Type == GREATER_EQUALS ||
		t.Type == NOT_EQUALS
}

// IsIdentifier returns true if the token is an identifier
func (t *Token) IsIdentifier() bool {
	return t.Type == IDENT || t.Type == UIDENT
}

// String returns a string representation of the token for debugging
func (t *Token) String() string {
	// tokenTypeNames provides string names for special token types (for debugging)
	// Most token names are now provided by the generated yyToknames array
	tokenTypeNames := map[int]string{
		INVALID: "INVALID",
		EOF:     "EOF",
	}

	typeName := tokenTypeNames[t.Type]
	if typeName == "" {
		if t.Type <= 126 && t.Type >= 32 {
			// ASCII character token
			typeName = string(rune(t.Type))
		} else {
			// Try to use parser-generated token names if available
			if t.Type > 0 && t.Type < len(yyToknames)+3 { // +3 for $end, error, $unk offset
				typeName = yyToknames[t.Type-4] // -4 for IDENT base offset in generated constants
			} else {
				typeName = fmt.Sprintf("UNKNOWN(%d)", t.Type)
			}
		}
	}

	return fmt.Sprintf("Token{Type: %s, Value: %v, Position: %d, Text: %q}",
		typeName, t.Value, t.Position, t.Text)
}
